<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>继承与原型链</title>
  </head>
  <body>
    <script>
      // 继承与原型链
      /**
       * 每个对象（object）都有一个私有属性指向另一个名为原型（prototype）的对象
       * 原型对象也是对象，所以原型对象也有一个自己的原型，层层向上直到一个对象的原型为 nul
       */

      // 基于原型链的继承
      /**
       * 符号 someObject.[[Prototype]] 用于标识 someObject 的原型
       * 内部插槽 [[Prototype]] 可以通过 Object.getPrototypeOf() 和 Object.setPrototypeOf() 函数来访问
       * 等同于 someObject.__proto__ 访问器
       */
      /**
       * 值得注意的是，{ __proto__: ... } 语法与 obj.__proto__ 访问器不同：前者是标准且未被弃用的。
       */
      const obj = {}; // Object 构造函数的 实例，默认原型对象是 实例的构造函数的prototype属性【原型属性】
      console.log(obj.__proto__);
      console.log(obj.__proto__ === Object.getPrototypeOf(obj));
      //   console.log(obj.__proto__ === Object.prototype);

      /**
       * 它不应与函数的 func.prototype 属性混淆，后者指定在给定函数被用作构造函数时分配给所有对象实例的 [[Prototype]]。
       * 我们将在后面的小节中讨论构造函数的原型属性。
       */
      // { a: 1, b: 2 } ---> { b: 3, c: 4 } ---> Object.prototype ---> null
      const o = {
        a: 1, // o.a 自有属性
        b: 2, // o.b 属性遮蔽
        __proto__: {
          b: 3,
          c: 5,
        },
      };
      console.log(o.b); // 2

      // 继承“方法”
      const parent = {
        value: 2,
        method() {
          return this.value + 1;
        },
      };

      const child = {
        __proto__: parent,
      };

      console.log(child.method()); // 3
      child.value = 4;
      console.log(parent.method() + "---" + child.method()); // 3---5

      // 构造函数
      /**
       * 会自动为每个构造的对象设置 [[Prototype]]。构造函数是使用 new 调用的函数
       */
      function Box(value) {
        this.value = value;
      }
      Box.prototype.getValue = function () {
        return this.value;
      };
      /**
       * Box.prototype 默认具有一个自有属性：constructor，它引用了构造函数本身。即，Box.prototype.constructor === Box。
       * 这允许我们在任何实例中访问原始构造函数。
       */
      console.log(Box.prototype);

      // 通过构造函数创建的每一个实例 都会自动将 构造函数的 prototype 属性 作为其 [[Prototype]]
      const box1 = new Box(1);
      console.log(box1.getValue()); // 1

      // 上面的构造函数可以重写为类

      // 有个推论是：重新赋值 Constructor.prototype（Constructor.prototype = ...）是一个不好的主意，原因有两点：
      Box.prototype.getValue = function () {
        return this.value + 1;
      };
      const box2 = new Box(1);
      console.log(box2.getValue()); // 2
      console.log(Object.getPrototypeOf(box2) === Object.getPrototypeOf(box1)); // true
      console.log(box1.constructor);

      // 字面量的隐式构造函数
      // 对象字面量（没有 `__proto__` 键）自动将
      // `Object.prototype` 作为它们的 `[[Prototype]]`
      const object = { a: 1 };
      Object.getPrototypeOf(object) === Object.prototype; // true
      // 数组字面量自动将 `Array.prototype` 作为它们的 `[[Prototype]]`
      const array = [1, 2, 3];
      Object.getPrototypeOf(array) === Array.prototype; // true
      // 正则表达式字面量自动将 `RegExp.prototype` 作为它们的 `[[Prototype]]`
      const regexp = /abc/;
      Object.getPrototypeOf(regexp) === RegExp.prototype; // true

      console.log("----------------------------------------------");

      // 构建更长的继承链
      function Base() {}
      function Derived() {}
      function Person() {}
      Object.setPrototypeOf(Derived.prototype, Base.prototype);
      console.log(Derived.prototype);
      const derived = new Derived();
      console.log(Object.getPrototypeOf(derived));

      // Object.create创建一个新对象，新对象的__proto__指向入参
      Person.prototype = Object.create(Base.prototype);
      Person.prototype.constructor = Person;
      console.log(Person.prototype);
      const person = new Person();
      console.log(person);
      console.log(Object.getPrototypeOf(person));
    </script>
  </body>
</html>
